package com.rajaev.dtu.tractor.domain.service;


import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.rajaev.dtu.tractor.app.service.BatteryRunInfoStaticService;
import com.rajaev.dtu.tractor.domain.entity.BatteryElectricLog;
import com.rajaev.dtu.tractor.domain.entity.BatteryOnlineLogPo;
import com.rajaev.dtu.tractor.domain.repository.mysql.BatteryElectricDao;
import com.rajaev.dtu.tractor.domain.repository.mysql.BatteryOnlineLogDao;
import com.rajaev.dtu.tractor.domain.vo.BatterySingleDeviceDailyStatistic;
import com.rajaev.dtu.tractor.domain.vo.BmsDailyStaticResult;
import com.rajaev.dtu.tractor.domain.vo.BmsHourlyStaticResult;
import com.rajaev.dtu.tractor.infrastructure.base.enums.BatteryElectricState;
import com.rajaev.dtu.tractor.infrastructure.base.enums.BatteryElectricType;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Created with IDEA
 * @author: tianjie
 * @Date:2019/6/13
 * @Time:10:28
 */
@Slf4j
@Service
public class BatteryRunInfoStaticServiceImpl implements BatteryRunInfoStaticService {


    @Autowired
    private BatteryOnlineLogDao batteryOnlineLogDao;

    @Autowired
    private BatteryElectricDao batteryElectricDao;

    private static final Cache<String,BatterySingleDeviceDailyStatistic> cache = CacheUtil.newTimedCache(1000 * 60);


    @Override
    public BmsDailyStaticResult staticDailyBattery(String vin, String customerId, Integer dataDate) {

        Date thisDate = DateUtil.parse(String.valueOf(dataDate), "yyyyMMdd");
        Date startTime = DateUtil.beginOfDay(thisDate);
        Date endTime = DateUtil.endOfDay(thisDate);

        List<BatteryOnlineLogPo> list = batteryOnlineLogDao.queryByVinAndDate(vin, startTime, endTime);
        AtomicLong atomicOnlineSeconds = new AtomicLong();
        for (BatteryOnlineLogPo batteryOnlineLogPo : list) {

            long onlineSeconds = Duration.between(batteryOnlineLogPo.getLogoutTime(), batteryOnlineLogPo.getLoginTime()).getSeconds();
            atomicOnlineSeconds.getAndAdd(onlineSeconds);
        }

        List<BatteryElectricLog> batteryElectricLogList = batteryElectricDao.selectElectricityByVinAndDate(vin, startTime, endTime);
        AtomicReference<BigDecimal> totalCharge = new AtomicReference<>(new BigDecimal("0"));
        AtomicReference<BigDecimal> totalDischarge = new AtomicReference<>(new BigDecimal("0"));
        AtomicLong chargeSeconds = new AtomicLong();
        AtomicLong dischargeSeconds = new AtomicLong();


        batteryElectricLogList.forEach(batteryElectricLog -> {
            if (batteryElectricLog.getState().equals(BatteryElectricState.FINISH)) {
                if (batteryElectricLog.getType().equals(BatteryElectricType.CHARGE)) {
                    Double electricity = batteryElectricLog.getElectricity();
                    totalCharge.getAndUpdate(bigDecimal -> bigDecimal.add(BigDecimal.valueOf(electricity)));
                    chargeSeconds.getAndAdd(DateUtil.between(batteryElectricLog.getEndTime(), batteryElectricLog.getStartTime(), DateUnit.SECOND));
                } else if (batteryElectricLog.getType().equals(BatteryElectricType.DISCHARGE)) {
                    Double electricity = batteryElectricLog.getElectricity();
                    totalDischarge.getAndUpdate(bigDecimal -> bigDecimal.add(BigDecimal.valueOf(electricity)));
                    dischargeSeconds.getAndAdd(DateUtil.between(batteryElectricLog.getEndTime(), batteryElectricLog.getStartTime(), DateUnit.SECOND));
                }
            }


        });
        BmsDailyStaticResult.BmsDailyStaticResultBuilder bmsDailyStaticResultBuilder = BmsDailyStaticResult.builder();
        bmsDailyStaticResultBuilder
                .chargeElectricity(totalCharge.get().doubleValue())
                .vin(vin)
                .customerId(customerId)
                .dataDate(dataDate)
                .online(atomicOnlineSeconds.get()>0)
                .dischargeElectricity(totalDischarge.get().doubleValue())
                .onlineTime(atomicOnlineSeconds.get())
                .dischargeTime(chargeSeconds.get())
                .chargeTime(dischargeSeconds.get());
        return bmsDailyStaticResultBuilder.build();

    }


    @Override
    public BatterySingleDeviceDailyStatistic staticBatterySingleDeviceDailyStatistic(String vin, Date date) {
        String key = DateUtil.format(date, "yyyyMMdd").concat(vin);

        return cache.get(key, () -> {
            Date beginOfDay = DateUtil.beginOfDay(date);
            List<BatteryOnlineLogPo> batteryOnlineLogPoList = batteryOnlineLogDao.queryByVinAndDate(vin, beginOfDay, date);
            List<BatteryElectricLog> batteryElectricLogList = batteryElectricDao.selectElectricityByVinAndDate(vin, beginOfDay, date);

            AtomicLong onlineSeconds = new AtomicLong();
            AtomicReference<BigDecimal> totalDischarge = new AtomicReference<>(new BigDecimal("0"));
            AtomicReference<BigDecimal> totalCharge = new AtomicReference<>(new BigDecimal("0"));

            if (CollectionUtils.isEmpty(batteryOnlineLogPoList)) {
                return null;
            }

            for (BatteryOnlineLogPo batteryOnlineLogPo : batteryOnlineLogPoList) {
                if (batteryOnlineLogPo.getLoginTime().isBefore(LocalDateTimeUtil.of(beginOfDay))) {
                    //当上线时间在今日凌晨之前，只统计当日的时间。
                    long onlineTime;
                    if (batteryOnlineLogPo.getLogoutTime() == null) {
                        onlineTime = LocalDateTimeUtil.between(LocalDateTimeUtil.now(), batteryOnlineLogPo.getLoginTime()).getSeconds();
                    } else {
                        onlineTime = LocalDateTimeUtil.between(batteryOnlineLogPo.getLogoutTime(), batteryOnlineLogPo.getLoginTime()).getSeconds();
                    }
                    onlineSeconds.getAndAdd(onlineTime);
                }
            }

            // 只计算计算当日的充电量和耗电量。
            for (BatteryElectricLog batteryElectricLog : batteryElectricLogList) {
                if (batteryElectricLog.getState().equals(BatteryElectricState.FINISH)) {
                    Double electricity = batteryElectricLog.getElectricity();
                    if (batteryElectricLog.getType().equals(BatteryElectricType.CHARGE)) {
                        totalCharge.getAndUpdate(ele -> ele.add(BigDecimal.valueOf(electricity)));
                    } else {
                        totalDischarge.getAndUpdate(ele -> ele.add(BigDecimal.valueOf(electricity)));
                    }
                }
            }
            BatterySingleDeviceDailyStatistic batterySingleDeviceDailyStatistic = new BatterySingleDeviceDailyStatistic(totalDischarge.get(), totalCharge.get(), onlineSeconds.get());
            return batterySingleDeviceDailyStatistic;
        });

    }

    @Override
    @Deprecated
    public BmsHourlyStaticResult staticHourlyBattery(String vin, String customerId, Integer dataDateHour) {
        return null;
    }


    /**
     * 电池运行数据统计
     */
    @Getter
    @Setter
    public static class RunInfo {

        /**
         * 充电电量
         */
        private BigDecimal charge;
        /**
         * 放电电量
         */
        private BigDecimal disCharge;

        /**
         * 在线时长，秒
         */
        private long onlineSeconds;
        /**
         * 充电时长,秒
         */
        private long chargeSeconds;
        /**
         * 放电时长,秒
         */
        private long dischargeSeconds;

    }

}
